package org.example;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.example.utils.AnnoUtil;
import org.example.utils.MyClassLoader;

import javax.servlet.annotation.WebServlet;
import java.io.File;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

public class BootStrap {

    private String appBase;
    private Integer port;

    public BootStrap() {
        try {
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();

            Element node = (Element) rootElement.selectSingleNode("//Host");
            appBase = node.attribute("appBase").getValue();
            Element node2 = (Element) rootElement.selectSingleNode("//Connector");
            port = Integer.valueOf(node2.attribute("port").getValue());
        } catch (Exception e) {
        }
    }


    public void start() throws Exception {
        loadServlet();
        loadContext();

        int corePoolSize = 10;
        int maximumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit, workQueue,
                threadFactory,
                handler);

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("-------启动成功--------");

        /**
         * 完成 MiniTomcat 1.0版本
         * 需求：浏览器请求http://localhost:8080,返回一个固定的字符串到页面"Hello Minicat!"
         */
       /*
        while (true) {
            Socket socket = serverSocket.accept();
            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();
            String data = "这是mino-tomcat主页";
            out.write((HttpProtocolUtil.getHttpHeader200() + data).getBytes());
            socket.close();
        }
        */

        /**
         * 完成Minicat 2.0版本
         * 需求：封装Request和Response对象，返回html静态资源文件
         */
/*
        while (true) {
            Socket socket = serverSocket.accept();
            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();
            Request request = new Request(in);
            System.out.println(request);
            Response response = new Response(out);
            response.outputHtml(request.getUrl());
            socket.close();
        }
 */

        /**
         * 完成Minicat 3.0版本
         * 需求：可以请求动态资源（Servlet）
         */
       /* while (true) {
            Socket socket = serverSocket.accept();
            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();
            Request request = new Request(in);
            Response response = new Response(out);
            System.out.println(request);
            HttpServlet httpServlet = servletMap.get(request.getUrl());
            if (httpServlet == null) {
                //静态资源处理
                response.outputHtml(request.getUrl());
            } else {
                httpServlet.service(request, response);
                // 动态资源servlet请求
            }
            socket.close();
        }*/

        /**
         * 多线程
         */
        /*while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
            requestProcessor.start();
        }*/

        //线程池
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
            threadPoolExecutor.execute(requestProcessor);
        }

    }

    Map<String, HttpServlet> servletMap = new ConcurrentHashMap<String, HttpServlet>();


    private void loadContext() throws Exception {

        File rootFile = new File(appBase);
        if (rootFile.exists()) {
        } else {
            return;
        }

        File[] contextFiles = rootFile.listFiles();
        for (File f : contextFiles) {
            MyClassLoader loader = new MyClassLoader();
            String p = f.getAbsolutePath() + "/WEB-INF/classes";
            File file = new File(p);
            if (file.exists()) {
                getSubFile(f.getName(), file.listFiles(), loader);
            }
        }
        for (String key : servletMap.keySet()) {
            HttpServlet httpServlet = servletMap.get(key);
            System.out.printf("key=%s,class=%s\n", key, httpServlet.getClass().getSimpleName());
        }
    }

    public void getSubFile(String appName, File[] files, MyClassLoader loader) throws Exception {
        for (File f : files) {
            if (f.isFile()) {
                String absolutePath = f.getAbsolutePath();
                absolutePath = AnnoUtil.replaceAllChar(absolutePath, '\\', '/');
                if (absolutePath.endsWith(".class")) {
                    String str = "/WEB-INF/classes/";
                    int i = absolutePath.indexOf(str) + str.length();
                    absolutePath = absolutePath.substring(i);
                    absolutePath = absolutePath.replace(".class", "");
                    String servletClass = AnnoUtil.replaceAllChar(absolutePath, '/', '.');
                    System.out.println(servletClass);

//                    Class<?> aClass = Class.forName(servletClass);
                    Class<?> aClass = loader.findClass(f.getAbsolutePath(), servletClass);


                    boolean b = aClass.isAnnotationPresent(WebServlet.class);
                    if (b) {
                        HttpServlet o = (HttpServlet) aClass.newInstance();
                        String[] value = aClass.getAnnotation(WebServlet.class).value();
                        for (String s : value) {
                            String url = String.format("/%s%s", appName, s);
                            servletMap.put(url, o);
                        }
                    }

                }
            } else {
                getSubFile(appName, f.listFiles(), loader);
            }
        }
    }

    private void loadServlet() throws Exception {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(inputStream);
        Element rootElement = document.getRootElement();

        List<Element> servletList = rootElement.selectNodes("//servlet");

        for (Element e : servletList) {
            // <servlet-name>MServlet</servlet-name>
            Element nodeName = (Element) e.selectSingleNode("servlet-name");
            //<servlet-class>org.example.MServlet</servlet-class>
            Element nodeClass = (Element) e.selectSingleNode("servlet-class");
            String servletName = nodeName.getTextTrim();
            String servletClass = nodeClass.getTextTrim();

            // 根据servlet-name的值找到url-pattern
            Element servletMapping = (Element) rootElement.selectSingleNode(String.format("/web-app/servlet-mapping[servlet-name='%s']", servletName));
            String url = servletMapping.selectSingleNode("url-pattern").getStringValue().trim();

            servletMap.put(url, (HttpServlet) Class.forName(servletClass).newInstance());
        }
    }


    public static void main(String[] args) {
        BootStrap miniTomcat = new BootStrap();
        try {
            miniTomcat.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
